home *** CD-ROM | disk | FTP | other *** search
Text File | 1991-12-15 | 33.6 KB | 1,116 lines |
- /* Copyright รก 1991 Gustavus Adolphus College. All rights reserved.
- *
- * Schematik was developed by Gustavus Adolphus College (GAC) with
- * support from NeXT Computer, Inc. Permission to copy this software,
- * to redistribute it, and to use it for any purpose is granted,
- * subject to the following restrictions and understandings.
- *
- * 1. Any copy made of this software must include this copyright
- * notice in full.
- *
- * 2. Users of this software agree to make their best efforts (a) to
- * return to the GAC Mathematics and Computer Science Department any
- * improvements or extensions that they make, so that these may be
- * included in future releases; and (b) to inform GAC of noteworthy
- * uses of this software.
- *
- * 3. All materials developed as a consequence of the use of this
- * software shall duly acknowledge such use, in accordance with the
- * usual standards of acknowledging credit in academic research.
- *
- * 4. GAC makes no express or implied warranty or representation of
- * any kind with respect to this software, including any warranty
- * that the operation of this software will be error-free. ANY
- * IMPLIED WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR
- * PURPOSE IS HEREBY DISCLAIMED. GAC is under no obligation to
- * provide any services, by way of maintenance, update, or otherwise.
- *
- * 5. In conjunction with products arising from the use of this
- * material, there shall be no use of the name of Gustavus Adolphus
- * College nor of any adaptation thereof in any advertising,
- * promotional, or sales literature without prior written consent
- * from GAC in each case.
- */
-
- #import "DocText.h"
- #import "../defines.h"
- #import DocWin_h
- #import FindAgent_h
- #import InteractionWin_h
- #import PrefAgent_h
- #import <appkit/Application.h>
- #import <appkit/Font.h>
- #import <appkit/Matrix.h>
- #import <appkit/Menu.h>
- #import <appkit/NXCursor.h>
- #import <appkit/NXImage.h>
- #import <appkit/ScrollView.h>
- #import <dpsclient/event.h>
- #import <sys/types.h>
- #import <nextdev/keycodes.h>
- #import <objc/List.h>
- #import <NXCType.h>
- #import <string.h>
- #import <streams/streams.h>
- #import <ctype.h>
- extern void usleep(unsigned);
- extern void NXBeep(void);
- extern void *malloc(), *realloc(), free();
-
- #define LEFT_PAREN '('
- #define RIGHT_PAREN ')'
- #define LEFT_BRACKET '['
- #define RIGHT_BRACKET ']'
- #define LEFT_BRACE '{'
- #define RIGHT_BRACE '}'
- #define LEFT_PARENS "([{"
- #define RIGHT_PARENS ")]}"
- #define PBLINKDELAY 175000
- #define COMMENTSTRING ";"
- #define COMMENTCHAR ';'
- #define BADLINEBEGINSET " \n\t\r\v\f)]};"
- #define DELIMETERSET " \n\t\r\v\f();\"\'`|[]{}\377"
- #define PREFIXCHARS "\'`,@#"
- #define BIG_INDENT 4
- #define SMALL_INDENT 2
- #define MAXTOK 1024
- #define MAX_FWD_ONLY 4
- #define TABWIDTH 8
- #define PAREN_NO_MATCH -1
- #define NOT_REALLY_PAREN -2
-
- #define larrow 172
- #define uarrow 173
- #define rarrow 174
- #define darrow 175
-
- typedef struct cntxt {
- struct cntxt *next;
- int elts;
- int distinguishedElts;
- int startPosition;
- int alignmentPosition;
- BOOL isLet;
- } Context;
-
- static BOOL inSet(const char, const char *);
- static int findParensMatch(id, const char, int, char *);
- static int findBeginningOfScope(id, int, char *);
- static int skipspacecommentnewline(const char[], int, int);
- static int skipstring(const char[], int, int);
- static int findEndOfScope(id, int, char *);
- static int getcolumnofposition(id, int);
- static void newElt(Context *, BOOL, NXStream *);
-
- static char *selectionBuffer=NULL;
-
- BOOL inOtherMethod=NO;
- int last1ClickPos=0;
-
- @implementation DocText
-
- #define PRE_CHANGE NXSelPt start,end; int tlength=0,i; BOOL wasInOtherMethod=inOtherMethod; \
- if (!wasInOtherMethod) \
- { inOtherMethod=YES; [super getSel:&start :&end]; tlength=textLength; }
-
- #define POST_CHANGE(POSITION,FROM,TO) \
- if (!wasInOtherMethod) \
- { inOtherMethod=NO; \
- for(i=[markerList count]-1; i; i--) \
- [[markerList objectAt:i] adjustTo:(POSITION) forPositionsFrom:(FROM) to:(TO) andAfterBy:(textLength-tlength)]; \
- if ([window isMemberOf:[InteractionWin class]]) \
- [[markerList objectAt:0] adjustTo:(POSITION) forPositionsFrom:(FROM) to:(TO) andAfterBy:(textLength-tlength)]; \
- [indentationState changeAt:FROM];}
-
- - initFrame:(NXRect *)r
- {
- return [self initFrame:r text:"" alignment:NX_LEFTALIGNED];
- }
-
- - initFrame:(NXRect *)r text:(const char *)text alignment:(int)mode
- {
- char *font=[[PrefAgent new] interFont];
-
- [super initFrame:r text:text alignment:mode];
- markerList=[[List alloc] initCount:1];
- [markerList addObject:[[Marker alloc] initAt:0 type:MARKRIGHT]];
- if (!(NXOrderStrings((unsigned char *)font, (unsigned char *)GOODFONT1, YES, strlen(GOODFONT1), NULL)&&
- NXOrderStrings((unsigned char *)font, (unsigned char *)GOODFONT2, YES, strlen(GOODFONT2), NULL)))
- [self setFont:[Font newFont:font size:[[PrefAgent new] interFontSize]]];
- else
- [self setFont:[Font newFont:GOODFONT1 size:11]];
- [self setSel:0 :0];
- indentationState = [[IndentationState alloc] initText:self];
- return self;
- }
-
- - free
- {
- [[markerList freeObjects] free];
- [indentationState free];
- return [super free];
- }
-
- - keyDown:(NXEvent *)theEvent
- {
- switch (theEvent->data.key.charCode)
- {
- case soh: if (theEvent->flags&NX_CONTROLMASK) // move to beginning of line
- {
- NXSelPt start,end; int pos;
- [super getSel:&start :&end];
- pos=[super positionFromLine:[super lineFromPosition:start.cp]];
- return [self setSel:pos :pos];
- }
- break;
- case stx: if (theEvent->flags&NX_CONTROLMASK) // move backward one character
- {
- theEvent->flags=NX_NUMERICPADMASK;
- theEvent->data.key.charSet=1;
- theEvent->data.key.charCode=larrow;
- }
- break;
- case etx: if (theEvent->flags&(NX_COMMANDMASK|NX_NUMERICPADMASK)) // Enter or Cntl-c
- return [[[[[[NXApp mainMenu] findCellWithTag:MENU_ACTIONS] target]
- itemList] selectCellWithTag:MENU_ACTIONS_Evaluate] sendAction];
- else if (theEvent->flags&NX_CONTROLMASK)
- theEvent->data.key.charCode=nul;
- break;
- case eot: if (theEvent->flags&NX_CONTROLMASK) // delete next character
- {
- NXSelPt start,end;
- [super getSel:&start :&end];
- [self setSel:start.cp+1 :start.cp+1]; // move cursor forward 1 char.
- theEvent->data.key.charCode=bs; // Text does a backspace
- }
- break;
- case enq: if (theEvent->flags&NX_CONTROLMASK) // move to end of line
- {
- NXSelPt start,end; int pos,line;
- [super getSel:&start :&end];
- line=[super lineFromPosition:start.cp];
- pos=([super positionFromLine:line+1]<0)?textLength:[super positionFromLine:line+1]-1;
- return [self setSel:pos :pos];
- }
- break;
- case ack: if (theEvent->flags&NX_CONTROLMASK) // move forward one character
- {
- theEvent->flags=NX_NUMERICPADMASK;
- theEvent->data.key.charSet=1;
- theEvent->data.key.charCode=rarrow;
- }
- break;
- case bel: if (theEvent->flags&NX_CONTROLMASK) // bell
- {
- NXBeep(); return self;
- }
- break;
- case bs : break; // delete previous character (implemented by Text)
- case ht : return [self formatTextSelectionOnly:YES]; // indent line
- case nl : break; // newline (non-indenting cr; implemented by Text)
- case vt : if (theEvent->flags&NX_CONTROLMASK) // delete forward to end of line
- {
- NXSelPt start,end; int pos,line;
- [super getSel:&start :&end];
- line=[super lineFromPosition:start.cp];
- pos=([super positionFromLine:line+1]<0)?textLength:[super positionFromLine:line+1]-1;
- [window disableFlushWindow];
- [self setSel:start.cp :pos];
- [window reenableFlushWindow];
- return [self cut:self];
- }
- break;
- case cr : {PRE_CHANGE // carriage return
- [window disableFlushWindow];
- [super keyDown:theEvent];
- [window reenableFlushWindow];
- POST_CHANGE(start.cp+1,start.cp,end.cp)}
- if (([[PrefAgent new] autoindent]&&!(theEvent->flags&NX_ALTERNATEMASK))||
- ((theEvent->flags&NX_ALTERNATEMASK)&&![[PrefAgent new] autoindent]))
- [self formatTextSelectionOnly:YES];
- [window flushWindow];
- return self;
- case so : if (theEvent->flags&NX_CONTROLMASK) // move down one line
- {
- theEvent->flags=NX_NUMERICPADMASK;
- theEvent->data.key.charSet=1;
- theEvent->data.key.charCode=darrow;
- }
- break;
- case dle: if (theEvent->flags&NX_CONTROLMASK) // move up one line
- {
- theEvent->flags=NX_NUMERICPADMASK;
- theEvent->data.key.charSet=1;
- theEvent->data.key.charCode=uarrow;
- }
- break;
- case em : if (theEvent->flags&NX_CONTROLMASK) // indent line (shift-tab)
- {
- return [self paste:self];
- }
- else
- return [self formatTextSelectionOnly:YES];
-
- case RIGHT_PAREN :
- case RIGHT_BRACKET:
- case RIGHT_BRACE :
- {PRE_CHANGE
- [super keyDown:theEvent];
- POST_CHANGE(start.cp,start.cp,end.cp)
- {char c;
- [super getSel:&start :&end];
- [super getSubstring:&c start:end.cp-1 length:1];
- if (inSet(c,RIGHT_PARENS)&&[[PrefAgent new] matchParens])
- {
- int pos;
- char buffer[textLength+1];
- [super getSubstring:buffer start:0 length:textLength];
- if ((pos = findParensMatch(self,(char)theEvent->data.key.charCode,start.cp-1,buffer))==PAREN_NO_MATCH)
- NXBeep();
- else if (pos >= 0)
- {
- [self setSel:pos :pos+1];
- NXPing();
- usleep(PBLINKDELAY);
- [self setSel:start.cp :start.cp];
- }
- }}}
- return self;
- }
- {PRE_CHANGE
- [super keyDown:theEvent];
- POST_CHANGE(start.cp,start.cp,end.cp)}
- return self;
- }
-
- - mouseDown:(NXEvent*)theEvent
- {
- NXSelPt start,end;
-
- switch (theEvent->data.mouse.click)
- {
- case 1 : [super mouseDown:theEvent];
- [self getSel:&start :&end];
- last1ClickPos = start.cp;
- return self;
- case 2 : [window disableFlushWindow];
- [super mouseDown:theEvent];
- {
- char c[4];
- [super getSel:&start :&end];
- [super getSubstring:c start:start.cp-2 length:3];
- if (inSet(c[2],LEFT_PARENS RIGHT_PARENS)&&!((c[0]=='#')&&(c[1]=='\\')))
- {
- [window reenableFlushWindow];
- if (([[PrefAgent new] dblClickMatch])&&((end.cp-start.cp)==1))
- {
- char buffer[textLength+1];
- int temp;
- [super getSubstring:buffer start:0 length:textLength];
- if ((temp = findParensMatch(self, buffer[start.cp], start.cp, buffer))<0)
- {
- [window flushWindow];
- return self;
- }
- if (inSet(buffer[start.cp], LEFT_PARENS))
- [super setSel:start.cp :temp+1];
- else
- [super setSel:temp :start.cp+1];
- }
- return self;
- }
- else if (inSet(c[2], DELIMETERSET)&&!((c[0]=='#')&&(c[1]=='\\')))
- {
- [[window reenableFlushWindow] flushWindow];
- return self;
- }
- else
- {
- int i,j;
- char buffer[textLength+1];
- [super getSubstring:buffer start:0 length:textLength];
- for(i=start.cp; (i>=0)&&(!inSet(buffer[i],DELIMETERSET)||((i>=2)&&(buffer[i-2]=='#')&&(buffer[i-1]=='\\'))); i--);
- for(j=end.cp; (j<textLength)&&(!inSet(buffer[j],DELIMETERSET)||((j>=2)&&(buffer[j-2]=='#')&&(buffer[j-1]=='\\'))); j++);
- [super setSel:i+1 :j];
- [[window reenableFlushWindow] flushWindow];
- }
- }
- return self;
- case 3 : break;
- default: [self selectScope2];
- return self;
- }
- return [super mouseDown:theEvent];
- }
-
- - paste:sender
- {
- PRE_CHANGE
- [super paste:sender];
- POST_CHANGE(end.cp+(textLength-tlength),start.cp,end.cp)
- if ([[PrefAgent new] formatAfterPaste])
- {
- [markerList addObject:[[Marker alloc] initAt:(end.cp+(textLength-tlength)) type:MARKCENTER]];
- [self setSel:start.cp :(end.cp+(textLength-tlength))];
- [self formatTextSelectionOnly:YES];
- end.cp = [[markerList objectAt:([markerList count]-1)] markerPosition];
- [self setSel:end.cp :end.cp];
- [[markerList removeLastObject] free];
- }
- return self;
- }
-
- - clear:sender
- {
- PRE_CHANGE
- [super clear:sender];
- POST_CHANGE(start.cp,start.cp,end.cp)
- return self;
- }
-
- - delete:sender
- {
- PRE_CHANGE
- [super delete:sender];
- POST_CHANGE(start.cp,start.cp,end.cp)
- return self;
- }
-
- - setText:(const char *)aString
- {
- PRE_CHANGE
- [super setText:aString];
- POST_CHANGE(0,0,tlength)
- return self;
- }
-
- - readText:(NXStream *)stream
- {
- PRE_CHANGE
- [super readText:stream];
- {
- NXStream *stream = [self stream];
- int c;
- while((c = NXGetc(stream)) != EOF)
- if(c == '\t'){
- int pos = NXTell(stream);
- int spaces = TABWIDTH - getcolumnofposition(self, pos-1) % TABWIDTH;
- char spacestring[spaces+1];
- [self setSel:pos-1 :pos];
- memset(spacestring, ' ', spaces);
- spacestring[spaces] = '\0';
- [self replaceSel:spacestring];
- stream = [self stream];
- NXSeek(stream, pos, NX_FROMSTART);
- }
- [self setSel:0 :0];
- }
- POST_CHANGE(0,0,tlength)
- return self;
- }
-
- - replaceSel:(const char *)aString length:(int)length
- {
- PRE_CHANGE
- [super replaceSel:aString length:length];
- POST_CHANGE(end.cp+(textLength-tlength),start.cp,end.cp)
- return self;
- }
-
- - readSelectionFromPasteboard:pboard
- {
- id ret;
- PRE_CHANGE
- ret = [super readSelectionFromPasteboard:pboard];
- POST_CHANGE(end.cp+(textLength-tlength),start.cp,end.cp)
- return ret;
- }
-
- - setFont:aFont
- {
- [window disableFlushWindow];
- [super setFont:aFont];
- [[[window display] reenableFlushWindow] flushWindow];
- [[window scrollView] setLineScroll:[super lineHeight]];
- [[window scrollView] setPageScroll:[super lineHeight]];
- return self;
- }
-
-
- - appendNewlineIfNeeded
- {
- NXSelPt start,end;
- int textPointer = [[markerList objectAt:0] markerPosition];
-
- if ([[PrefAgent new] transcriptMode])
- [[markerList objectAt:0] adjustTo:textLength-1];
- [super getSel:&start :&end];
- [markerList addObject:[[Marker alloc] initAt:end.cp type:MARKCENTER]];
- [markerList addObject:[[Marker alloc] initAt:start.cp type:MARKCENTER]];
- if (textPointer>0)
- {
- char buffer[2];
- [super getSubstring:buffer start:textPointer-1 length:1];
- if (buffer[0]!=NEWLINECHAR)
- {
- [[self setSel:textPointer :textPointer] replaceSel:NEWLINESTRING];
- [window setDocEdited:YES];
- [self setInsertionBar:textPointer+1];
- }
- }
- start.cp = [[markerList objectAt:([markerList count]-1)] markerPosition];
- end.cp = [[markerList objectAt:([markerList count]-2)] markerPosition];
- [self setSel:start.cp :end.cp];
- [[markerList removeLastObject] free];
- [[markerList removeLastObject] free];
- return self;
- }
-
- - appendText:(const char *)aString commented:(BOOL)aBoolean withNewline:(BOOL)aBoolean2
- {
- NXSelPt start,end;
- int textPointer = [[markerList objectAt:0] markerPosition],i;
-
- if ([[PrefAgent new] transcriptMode])
- [[markerList objectAt:0] adjustTo:textLength-1];
- [super getSel:&start :&end];
- [markerList addObject:[[Marker alloc] initAt:end.cp type:MARKCENTER]];
- [markerList addObject:[[Marker alloc] initAt:start.cp type:MARKCENTER]];
- [window disableFlushWindow];
- if (aBoolean)
- {
- [super setSel:textPointer :textPointer];
- if ([super positionFromLine:[super lineFromPosition:textPointer]]==textPointer)
- [self replaceSel:COMMENTSTRING];
- for(i=0; aString[i]; i++)
- {
- [self replaceSel:(aString+i) length:1];
- if ((aString[i]==NEWLINECHAR)&&(aString[i+1])&&(aString[i+1]!=NEWLINECHAR)&&(aString[i+1]!=COMMENTCHAR))
- [self replaceSel:COMMENTSTRING];
- }
- }
- else
- [[self setSel:textPointer :textPointer] replaceSel:aString];
- if (aBoolean2)
- [self replaceSel:NEWLINESTRING];
- [window setDocEdited:YES];
- if ([[PrefAgent new] autoScroll])
- [super scrollSelToVisible];
- start.cp = [[markerList objectAt:([markerList count]-1)] markerPosition];
- end.cp = [[markerList objectAt:([markerList count]-2)] markerPosition];
- [self setSel:start.cp :end.cp];
- [[markerList removeLastObject] free];
- [[markerList removeLastObject] free];
- [[window reenableFlushWindow] flushWindow];
- return self;
- }
-
- - (STR)getAll
- {
- [super selectAll:self];
- return [self getSelection];
- }
-
- - (STR)getSelection
- {
- NXSelPt start,end;
- int length;
-
- [super getSel:&start :&end];
- length = end.cp-start.cp;
- selectionBuffer = (char *)(selectionBuffer==NULL?malloc(length+1):realloc(selectionBuffer, length+1));
- [super getSubstring:selectionBuffer start:start.cp length:length];
- selectionBuffer[length] = '\0';
- if ([window isMemberOf:[InteractionWin class]]&&(*selectionBuffer))
- if ([[PrefAgent new] transcriptMode])
- [self setInsertionBar:textLength];
- else
- [self setInsertionBar:end.cp];
- return selectionBuffer;
- }
-
- - setInsertionBar:(int)anInt
- {
- int textPointer = (textLength<=anInt?textLength-1:anInt);
- [[markerList objectAt:0] adjustTo:textPointer];
- return [super setSel:textPointer :textPointer];
- }
-
- - (BOOL)selectScope
- {
- NXSelPt start,end;
- int startpos,endpos;
- char textBuffer[textLength+1];
-
- [super getSel:&start :&end];
- [super getSubstring:textBuffer start:0 length:textLength];
- textBuffer[textLength] = '\0';
- if (((startpos = findBeginningOfScope(self, start.cp, textBuffer))<0)||((endpos = findEndOfScope(self, startpos, textBuffer))<0)||(endpos<end.cp))
- return NO;
- [super setSel:startpos :endpos];
- return YES;
- }
-
- - (BOOL)selectScope2
- {
- int startpos,endpos;
- char textBuffer[textLength+1];
-
- [super getSubstring:textBuffer start:0 length:textLength];
- textBuffer[textLength] = '\0';
- if (((startpos = findBeginningOfScope(self, last1ClickPos, textBuffer))<0)||((endpos = findEndOfScope(self, startpos, textBuffer))<0)||(endpos<last1ClickPos))
- return NO;
- [super setSel:startpos :endpos];
- return YES;
- }
-
- - saveSelectionState
- {
- NXSelPt start,end;
- [super getSel:&start :&end];
- [markerList addObject:[[Marker alloc] initAt:end.cp type:MARKCENTER]];
- [markerList addObject:[[Marker alloc] initAt:start.cp type:MARKCENTER]];
- return self;
- }
-
- - restoreSelectionState
- {
- int start,end;
- if ([markerList count]<3) return nil;
- start = [[markerList objectAt:([markerList count]-1)] markerPosition];
- end = [[markerList objectAt:([markerList count]-2)] markerPosition];
- [self setSel:start :end];
- [[markerList removeLastObject] free];
- [[markerList removeLastObject] free];
- return self;
- }
-
- - (int)stringSearch:(const char *)theTarget forAgent:theAgent direction:(int)direction ignoreCase:(BOOL)ignorecase wholeWord:(BOOL)wholeword inSel:(BOOL)inSel
- {
- NXSelPt start,end;
- int slength,tlength = strlen(theTarget);
- int startcp,endcp,i,origTextLength=textLength;
- char *buffer;
-
- [super getSel:&start :&end];
- slength = end.cp-start.cp;
- if (inSel)
- {
- buffer = (char *)NXZoneMalloc([theAgent zone], slength+1);
- [super getSubstring:buffer start:start.cp length:slength];
- startcp = 0;
- endcp = (slength-tlength);
- }
- else
- {
- buffer = (char *)NXZoneMalloc([theAgent zone], origTextLength+1);
- [super getSubstring:buffer start:0 length:origTextLength];
- startcp = (direction==FINDFORWARD?end.cp:start.cp-tlength);
- endcp = (direction==FINDFORWARD?origTextLength-tlength+1:0);
- }
- for(i=startcp; (direction==FINDFORWARD?i<=endcp:i>=endcp); i+=direction)
- if (!NXOrderStrings((unsigned char *)theTarget,(unsigned char *)(buffer+i),!ignorecase,tlength,NULL))
- if (!(wholeword&&(((i>0)&&NXIsAlNum(buffer[i-1]))||(((i+tlength)<=(inSel?slength:origTextLength))&&NXIsAlNum(buffer[i+tlength])))))
- {
- int offset=i+(inSel?start.cp:0)+(textLength-origTextLength);
- [super setSel:offset :(offset+tlength)];
- NXZoneFree([theAgent zone], buffer);
- return 1;
- }
- if (inSel)
- {
- NXZoneFree([theAgent zone], buffer);
- return 0;
- }
- startcp = (direction==FINDFORWARD?0:textLength-tlength+1);
- endcp = (direction==FINDFORWARD?end.cp-2:start.cp+1);
- origTextLength = textLength;
- for(i=startcp; (direction==FINDFORWARD?i<=endcp:i>=endcp); i+=direction)
- if (!NXOrderStrings((unsigned char *)theTarget,(unsigned char *)(buffer+i),!ignorecase,tlength,NULL))
- if (!(wholeword&&(((i>0)&&NXIsAlNum(buffer[i-1]))||(((i+tlength)<=origTextLength)&&NXIsAlNum(buffer[i+tlength])))))
- {
- int offset=i+(textLength-origTextLength);
- [super setSel:offset :(offset+tlength)];
- NXZoneFree([theAgent zone], buffer);
- return 1;
- }
- NXZoneFree([theAgent zone], buffer);
- return 0;
- }
-
- - formatTextSelectionOnly:(BOOL)selOnly
- {
- NXSelPt start, end;
- int startline, finishline, i;
-
- [self getSel:&start :&end];
- [markerList addObject:[[Marker alloc] initAt:end.cp type:MARKRIGHT]];
- [markerList addObject:[[Marker alloc] initAt:start.cp type:MARKRIGHT]];
-
- if (!selOnly && start.cp >= end.cp){
- startline = 1;
- finishline = [self lineFromPosition:textLength];
- } else {
- char c;
- startline = [self lineFromPosition:start.cp];
- [self getSubstring:&c start:end.cp-1 length:1];
- finishline = [self lineFromPosition: ((c == '\n') && (start.cp < end.cp)
- ? (end.cp - 1)
- : end.cp)];
- }
-
- for (i = startline; i <= finishline; i++) {
- int desired = [indentationState determineIndentation:i];
-
- if (desired >= 0){
- BOOL anyTabs = NO;
- int current;
- unsigned char c;
- NXStream *stream = [self stream];
-
- NXSeek(stream, [self positionFromLine:i], NX_FROMSTART);
- for (current = 0; ; current++){
- if((c = NXGetc(stream)) == '\t')
- anyTabs = YES;
- else if (c != ' ')
- break;
- }
- #ifdef DEBUG_INDENTATION
- fprintf(stderr, "Linen %d; current = %d, desired = %d%s\n",
- i, current, desired, anyTabs?"; tabs present":"");
- #endif
- if(anyTabs || current != desired){
- int startpos = [self positionFromLine:i];
- char spacestring[desired+1];
-
- [self setSel:startpos :startpos+current];
- memset(spacestring, ' ', desired);
- spacestring[desired] = '\0';
- [self replaceSel:spacestring];
- [window setDocEdited:YES];
- }
- else
- NXUngetc(stream);
- }
- }
- start.cp = [[markerList objectAt:([markerList count]-1)] markerPosition];
- end.cp = [[markerList objectAt:([markerList count]-2)] markerPosition];
- [self setSel:start.cp :end.cp];
- [[markerList removeLastObject] free];
- [[markerList removeLastObject] free];
- return self;
- }
-
- @end
-
- static BOOL inSet(const char element, const char *set)
- {
- int i;
-
- for(i=0; *(set+i); i++)
- if (element==*(set+i))
- return YES;
- return NO;
- }
-
- static int findParensMatch(id self, const char parens, int startpos, char *text)
- {
- int i,count=0,init,match,direction,textlength=[self textLength];
-
- switch (init=parens)
- {
- case LEFT_PAREN : match = RIGHT_PAREN; direction = 1; break;
- case LEFT_BRACKET : match = RIGHT_BRACKET; direction = 1; break;
- case LEFT_BRACE : match = RIGHT_BRACE; direction = 1; break;
- case RIGHT_PAREN : match = LEFT_PAREN; direction = -1; break;
- case RIGHT_BRACKET: match = LEFT_BRACKET; direction = -1; break;
- case RIGHT_BRACE : match = LEFT_BRACE; direction = -1; break;
- default : return NOT_REALLY_PAREN;
- }
- if ((text[startpos-2]=='#')&&(text[startpos-1]=='\\'))
- return NOT_REALLY_PAREN;
- for(i=startpos+direction; ((i>=0)&&(i<textlength))&&((count>0)||(text[i]!=match)||((i>=2)&&(text[i-2]=='#')&&(text[i-1]=='\\'))); i+=direction)
- {
- if ((direction>0)&&(text[i]=='\"')&&!((text[i-2]=='#')&&(text[i-1]=='\\')))
- {
- i=skipstring(text,textlength,i);
- if (i<0) break;
- }
- else if ((direction>0)&&(text[i]==COMMENTCHAR)&&!((text[i-2]=='#')&&(text[i-1]=='\\')))
- i=skipspacecommentnewline(text,textlength,i-1)-1;
- else if ((text[i]==init)&&!((text[i-2]=='#')&&(text[i-1]=='\\')))
- count++;
- else if ((text[i]==match)&&!((text[i-2]=='#')&&(text[i-1]=='\\')))
- count--;
- }
- return ((i>=textlength)||(i<0)?PAREN_NO_MATCH:i);
- }
-
- static int findBeginningOfScope(id self, int currpos, char *text)
- {
- int line,startpos;
-
- line = [self lineFromPosition:currpos];
- startpos = [self positionFromLine:line];
- for(; line&&(!text[startpos]||inSet(text[startpos],BADLINEBEGINSET)); startpos = [self positionFromLine:--line]);
- return (line<=0?-1:startpos);
- }
-
- static int skipspacecommentnewline(const char buffer[], int textlength, int endpos)
- {
- for(endpos++; (endpos<textlength)&&(buffer[endpos]!=NEWLINECHAR)&&NXIsSpace(buffer[endpos]); endpos++);
- if (buffer[endpos]==NEWLINECHAR)
- return endpos+1;
- else if ((buffer[endpos]==COMMENTCHAR)&&!((endpos>=2)&&(buffer[endpos-2]=='#')&&(buffer[endpos-1]=='\\')))
- {
- for(endpos++; (endpos<textlength)&&(buffer[endpos]!=NEWLINECHAR); endpos++);
- if (endpos>=textlength) return endpos;
- return endpos+1;
- }
- else
- return endpos;
- }
-
- static int skipstring(const char buffer[], int textlength, int endpos)
- {
- if ((endpos>=2)&&(buffer[endpos-1]=='\\')&&(buffer[endpos-2]=='#'))
- return endpos;
- while (1)
- {
- for(endpos++; (endpos<textlength)&&(buffer[endpos]!='\"'); endpos++);
- if (endpos>=textlength) return -1;
- if ((buffer[endpos-1]!='\\')||((endpos>=2)&&(buffer[endpos-2]=='\\'))) return endpos;
- }
- }
-
- static int findEndOfScope(id self, int startpos, char *text)
- {
- int endpos=0;
-
- for(; text[startpos]&&inSet(text[startpos],PREFIXCHARS); startpos++);
- if ((text[startpos]=='\\')&&(text[startpos-1]=='#'))
- startpos--;
- if (inSet(text[startpos],LEFT_PARENS))
- {
- if ((endpos = findParensMatch(self,text[startpos],startpos,text))<0)
- return -1;
- }
- else if (text[startpos]=='\"')
- {
- if ((endpos = skipstring(text,strlen(text),startpos))<0)
- return -1;
- }
- else
- {
- for(endpos=startpos; text[endpos]&&(!inSet(text[endpos],DELIMETERSET)||((text[endpos-2]=='#')&&(text[endpos-1]=='\\'))); endpos++);
- endpos--;
- }
- endpos = skipspacecommentnewline(text, strlen(text), endpos);
- if (endpos<=startpos) return -1;
- return endpos;
- }
-
- static int getcolumnofposition(id self, int charpos)
- {
- return charpos-[self positionFromLine:[self lineFromPosition:charpos]];
- }
-
-
- @implementation Marker
-
- - init
- {
- return [self initAt:0 type:MARKCENTER];
- }
-
- - initAt:(int)anInt type:(signed char)aChar
- {
- [super init];
- charpos = anInt;
- type = aChar;
- return self;
- }
-
- - (int)markerPosition
- {
- return charpos;
- }
-
- - adjustTo:(int)anInt forPositionsFrom:(int)begin to:(int)end andAfterBy:(int)anInt2
- {
- if (((charpos==begin)&&(type==MARKRIGHT))||((begin<charpos)&&(charpos<end))||((charpos==end)&&(type==MARKLEFT)))
- charpos = anInt;
- else if (end<=charpos)
- charpos += anInt2;
- return self;
- }
-
- - adjustTo:(int)anInt
- {
- charpos = anInt;
- return self;
- }
-
- @end
-
- @implementation IndentationState
-
- - changeAt:(int)position
- {
- stream = NULL;
- if ( [text lineFromPosition:position] < knownLine ){
- knownLine = 1;
- while(contexts){
- Context *temp = contexts;
- contexts = temp->next;
- free(temp);
- }
- inString = NO;
- }
- return self;
- }
-
- - init
- {
- return [self initText:nil];
- }
-
- - initText:theText
- {
- [super init];
- text = theText;
- knownLine = 1;
- stream = NULL;
- contexts = NULL;
- inString = NO;
- keywordListVersion = [[PrefAgent new] keywordListVersion];
- return self;
- }
-
- - free
- {
- while(contexts)
- {
- Context *temp = contexts;
- contexts = temp->next;
- free(temp);
- }
- return [super free];
- }
-
- - (int)determineIndentation:(int)line
- {
- int currentKeywordListVersion = [[PrefAgent new] keywordListVersion];
-
- if (keywordListVersion != currentKeywordListVersion){
- keywordListVersion = currentKeywordListVersion;
- [self changeAt:0];
- }
- if (line < knownLine || (line - knownLine) > MAX_FWD_ONLY){
- int l;
- for(l = line-1; l > 1 && l != knownLine; l--){
- char c;
- [text getSubstring:&c start:[text positionFromLine:l] length:1];
- if(!index(BADLINEBEGINSET, c))
- break;
- }
- if(l == 0)
- l++;
- if(l != knownLine){
- knownLine = l;
- stream = NULL;
- inString = NO;
- while(contexts){
- Context *temp = contexts;
- contexts = temp->next;
- free(temp);
- }
- }
- }
- while(knownLine < line)
- [self scanLine];
- return [self indent];
- }
-
- - setupStream
- {
- if (stream == NULL){
- stream = [text stream];
- NXSeek(stream, [text positionFromLine:knownLine], NX_FROMSTART);
- }
- return self;
- }
-
- - (int)indent
- {
- #ifdef DEBUG_INDENTATION
- fprintf(stderr, "start of IndentationState/indent ");
- [self describeSelf];
- #endif
- if (inString)
- return -1;
- else if (contexts == NULL)
- return 0;
- else {
- Context *c = contexts;
- if (c->elts < c->distinguishedElts + 1)
- return BIG_INDENT + getcolumnofposition(text, c->startPosition);
- else if (c->elts == c->distinguishedElts + 1)
- return SMALL_INDENT + getcolumnofposition(text, c->startPosition);
- else
- return getcolumnofposition(text, c->alignmentPosition);
- }
- }
-
- - scanLine
- {
- BOOL anythingYet = NO;
- Context *context = contexts;
- BOOL prefix = NO;
- char c;
-
- #ifdef DEBUG_INDENTATION
- fprintf(stderr, "start of IndentationState/scanLine ");
- [self describeSelf];
- #endif
- [self setupStream];
- knownLine++;
- if (inString)
- goto scan_string;
- while (1){
- switch (c = NXGetc(stream)){
- case '\n': case '\377':
- return self;
- case ' ': case '\t':
- continue;
- case ';':
- while(NXGetc(stream) != '\n')
- ;
- return self;
- case '\'': case'`': case ',': case '@': case '#':
- if(!prefix){
- prefix = YES;
- newElt(context, anythingYet, stream);
- }
- if(c == '#'){
- if(NXGetc(stream) == '\\'){
- if(isalpha(c = NXGetc(stream))){
- while(index(DELIMETERSET, NXGetc(stream)) == NULL)
- ;
- NXUngetc(stream);
- break;
- } else if(c == '\n')
- NXUngetc(stream);
- else
- break;
- } else
- NXUngetc(stream);
- }
- continue;
- case '(': case '[': case '{':
- if(!prefix)
- newElt(context, anythingYet, stream);
- contexts = malloc(sizeof(Context));
- ((Context *)contexts)->next = context;
- context = contexts;
- context->isLet = NO;
- context->elts = 0;
- context->distinguishedElts = -1;
- context->startPosition = (context->alignmentPosition=NXTell(stream)) - 1;
- break;
- case ')': case ']': case '}':
- if(context != NULL){
- contexts = context->next;
- free(context);
- context = contexts;
- }
- break;
- case '"':
- inString = YES;
- if(!prefix)
- newElt(context, anythingYet, stream);
- scan_string:
- while (1){
- if ((c = NXGetc(stream)) == '\n')
- return self;
- else if (c == '\\'){
- if ((c = NXGetc(stream)) == '\n')
- NXUngetc(stream);
- } else if (c == '"'){
- inString = NO;
- break;
- }
- }
- break;
- default:
- #ifdef DEBUG_INDENTATION
- fprintf(stderr, "start of token ");
- [self describeSelf];
- #endif
- if(!prefix)
- newElt(context, anythingYet, stream);
- if(!prefix && context != NULL && context->elts == 2
- && context->isLet)
- context->distinguishedElts++;
- if (!prefix && context != NULL && context->elts == 1) {
- char tokbuf[MAXTOK+1];
- int i = 0;
- do {
- if (i < MAXTOK)
- tokbuf[i++] = c;
- } while(index(DELIMETERSET, c = NXGetc(stream)) == NULL);
- NXUngetc(stream);
- tokbuf[i] = '\0';
- context->distinguishedElts = [[PrefAgent new] deForSpecialForm:tokbuf];
- if(!NXOrderStrings((unsigned char *) tokbuf, (unsigned char *) "let",
- NO, -1, NULL))
- context->isLet = YES;
- } else{
- while(index(DELIMETERSET, NXGetc(stream)) == NULL)
- ;
- NXUngetc(stream);
- }
- #ifdef DEBUG_INDENTATION
- fprintf(stderr, "end of token ");
- [self describeSelf];
- #endif
- break;
- }
- prefix = NO;
- anythingYet = YES;
- }
- }
-
- - describeSelf
- {
- Context *context = contexts;
- fprintf(stderr, "indentation state\n");
- fprintf(stderr, " knownLine: %d\n", knownLine);
- if(stream == NULL)
- fprintf(stderr, " no stream\n");
- else{
- int pos = NXTell(stream);
- fprintf(stderr, " stream position: %d (row %d, col %d)\n",
- pos, [text lineFromPosition:pos],
- getcolumnofposition(text, pos));
- }
- fprintf(stderr, " %sin string\n", inString?"":"not ");
- while(context){
- fprintf(stderr, " elts: %d, des: %d, startpos: %d, alignpos: %d\n",
- context->elts, context->distinguishedElts, context->startPosition,
- context->alignmentPosition);
- context = context->next;
- }
- return self;
- }
-
- @end
-
- static void newElt(Context *context, BOOL anythingYet, NXStream *stream){
- if (context == NULL)
- return;
- if(context->elts == 1 || (context->elts > 1 && !anythingYet))
- context->alignmentPosition = NXTell(stream)-1;
- context->elts++;
- }
-